#include <algorithm>
#include <gmVariable.h>
#include <gmThread.h>
#include <gmCall.h>
#include "Server.h"
#include "Packet.h"

void Delay(int milliseconds)
{
    #ifdef WIN32
    Sleep(milliseconds);
    #else
    usleep(milliseconds * 1000);
    #endif
}


Server::Server():
    m_machine(new gmMachine), m_players(m_machine), m_listenSocket(new sox::ListenSock),
    m_isRunning(true) {}

int Server::Run(int argc, char* argv[])
{
    GMPlayer::Register(m_machine);
    GMPlayerList::Register(m_machine);
    GMClient::Register(m_machine);

    gmuint32 deltaTime = 0;
    gmuint32 frameStart = GetTickCount();

    while(m_isRunning)
    {
        //Limit the amount of unverified connections
        while(m_unknownClients.size() > 60)
        {
            Client* client = m_unknownClients.front();
            delete client;
            m_unknownClients.pop_front();
        }

        //Accept new player
        sox::TcpSocket* newSocket = 0;
        while((newSocket = m_listenSocket->Accept()) && m_unknownClients.size() < 70)
        {
            GMClient* client = new GMClient(m_machine, newSocket);
            m_unknownClients.push_back(client);
        }

        if(m_unknownClients.size())
        {
            sox::SoxPoller poller;

            //Build list of sockets (to check for incoming data)
            for(std::list<GMClient*>::iterator it = m_unknownClients.begin(); it != m_unknownClients.end();)
            {
                GMClient* client = *it;

                if(client->GetTimeout() < 20)
                {
                    poller.AddSocket(client->GetSocket(), POLL_READ);
                    ++it;
                } else {
                    it = m_unknownClients.erase(it);
                    delete client;
                }
            }

            //Poll the sockets (determines which of the sockets has data to receive)
            //max wait = 1 milliseconds (1000 microseconds)
            if(poller.Poll(1000) > 0)
            {
                //Loop through the player sockets and check if they have any data to read
                for(std::list<GMClient*>::iterator it = m_unknownClients.begin(), next = it; it != m_unknownClients.end();  it = next)
                {
                    ++next;
                    GMClient* client = *it;

                    //Can we receive any data?
                    if(poller.IsSet(client->GetSocket(), POLL_READ))
                    {
                        //Receive the first packet
                        client->RecvPackets();

                        //Parse it
                        ByteStream packet;
                        if(client->ReadPacket(&packet))
                            ParseFirstPacket(client, packet);
                        else delete client;

                        m_unknownClients.erase(it);
                    }

                }
            }
        }

        //Receive all incoming packets from players
        if(m_players.PlayerCount())
        {
            sox::SoxPoller poller;
            for(size_t i = 0; i < m_players.PlayerCount(); ++i)
            {
                GMPlayer* player = m_players.GetAt(i);
                poller.AddSocket(player->GetClient()->GetSocket(), POLL_READ);
            }

            if(poller.Poll(1000) > 0)
            {
                for(size_t i = 0; i < m_players.PlayerCount(); ++i)
                {
                    GMPlayer* player = m_players.GetAt(i);

                    if(poller.IsSet(player->GetClient()->GetSocket(), POLL_READ))
                    {
                        player->GetClient()->RecvPackets();

                        //Parse all the packets
                        for(ByteStream packet; player->GetClient()->ReadPacket(&packet);)
                        {
                            //Separate the packet into lines (using \n as the end of line)
                            const char* last    = &packet[0] + packet.Length();
                            const char* current = &packet[0], *token = std::find(current, last, '\n');

                            while(token != last)
                            {
                                ByteStream packet(current, size_t(token - current));
                                ParsePackage(player, packet);
                                current = token + 1;
                                token = std::find(current, last, '\n');
                            }
                        }
                    }
                }
            }
        }

        m_machine->Execute(deltaTime);
        deltaTime = GetTickCount() - frameStart;
        frameStart = frameStart + deltaTime;
    }
    return 0;
}

void Server::RegisterEventHandler(int pluginID, int eventID, const GMEvent& event)
{
    std::map<int, GMEvent>& eventHandles = m_eventHandlers[eventID];
    eventHandles[pluginID] = event;
}


bool Server::PrepareEvent(int eventID)
{
    std::map<int, std::map<int, GMEvent> >::iterator it = m_eventHandlers.find(eventID);

    if(it != m_eventHandlers.end())
    {
        m_currentEvent = &it->second;
        return true;
    }
    return false;
}

void Server::SendAccount(GMPlayer* player)
{
    ByteStream packet;

    //Allow more than 8 players
    player->WritePacket(NewPacket(UNLIMITEDSIG) << PacketInt1(73));


}


void Server::CallEvent(gmVariable args[], int argc)
{
    for(std::map<int, GMEvent>::iterator it2 = m_currentEvent->begin(); it2 != m_currentEvent->end(); ++it2)
    {
        gmCall call;
        GMEvent& event = it2->second;

        if(event.m_function)
        {
            if(call.BeginFunction(m_machine, event.m_function, event.m_thisObject, false))
            {
                for(int i = 0; i < argc; ++i)
                    call.AddParam(args[i]);

                call.End();
            }
        }
    }
}

GMPlayer* Server::AllocPlayer(GMClient* client)
{
    for(size_t i = 1; i < 2048; ++i)
    {
        GMPlayer* player = m_players.FindByID(i);
        if(player == NULL)
        {
            GMPlayer* player = new GMPlayer(m_machine, i, client);
            m_players.AddPlayer(i, player);
            return player;
        }
    }
    return NULL;
}

void Server::ParseFirstPacket(GMClient* client, ByteStream& packet)
{
    int clientType = packet.Read<PacketInt1>().Value();

    if(clientType == 5)
        client->UpdatePacketVersion(packet.Read<PacketInt1>().Value());

    std::string clientVersion   = packet.ReadString(8);
    std::string accountName     = packet.ReadString(packet.Read<PacketInt1>().Value());
    std::string password        = packet.ReadString(packet.Read<PacketInt1>().Value());

    if(PrepareEvent(EVENT_NEW_CLIENT))
    {
        gmVariable args[] = {
            gmVariable(client->GetUserObject()),
            gmVariable(clientType),
            gmVariable(m_machine->AllocStringObject(accountName.c_str())),
            gmVariable(m_machine->AllocStringObject(password.c_str()))
        };

        CallEvent(args, 4);
    } else {
        GMPlayer* player = AllocPlayer(client);
        player->SetProperty(PLAYER_PROP_ACCOUNT, gmVariable(m_machine->AllocStringObject(accountName.c_str())));
        player->SetProperty(PLAYER_PROP_LEVELNAME, gmVariable(m_machine->AllocStringObject("onlinestartlocal.graal")));
        player->SetProperty(PLAYER_PROP_X, gmVariable(32.0f));
        player->SetProperty(PLAYER_PROP_Y, gmVariable(32.0f));

        SendAccount(player);
    }


}

void Server::ParsePackage(Player* player, ByteStream& packet)
{
}


///GMServer
gmType GMServer::m_userType                     = GM_NULL;
static gmStringObject* GMSTRING_PLAYER_COUNT    = NULL;
static gmStringObject* GMSTRING_PLAYERS         = NULL;



bool GM_CDECL GMServer::GMTrace(gmMachine * a_machine, gmUserObject* a_object, gmGarbageCollector* a_gc, const int a_workLeftToGo, int& a_workDone)
{
    GMServer* thisObject = static_cast<GMServer*>(a_object->m_user);

    for(std::map<int, std::map<int, GMEvent> >::iterator it = thisObject->m_eventHandlers.begin(); it != thisObject->m_eventHandlers.end(); ++it)
    {
        std::map<int, GMEvent>& event = it->second;

        for(std::map<int, GMEvent>::iterator it2 = event.begin(); it2 != event.end(); ++it2)
        {
            GMEvent& gmEvent = it2->second;

            if(gmEvent.m_function) {
                a_gc->GetNextObject(gmEvent.m_function);
                a_workDone++;
            }

            if(gmEvent.m_thisObject.IsReference()) {
                a_gc->GetNextObject((gmObject*)gmEvent.m_thisObject.m_value.m_ref);
                a_workDone++;
            }
        }
    }

    for(std::list<GMClient*>::iterator it = thisObject->m_unknownClients.begin(); it != thisObject->m_unknownClients.end(); ++it)
    {
        GMClient* client = *it;
        a_gc->GetNextObject(client->GetUserObject());
        a_workDone++;
    }

    for(size_t i = 0; i < thisObject->m_players.PlayerCount(); ++i)
    {
        a_gc->GetNextObject(thisObject->m_players.GetAt(i)->GetUserObject());
        a_workDone++;
    }
    a_workDone++;
    return true;
}

int GM_CDECL GMServer::GMRegisterEvent(gmThread *a_thread)
{
    if(a_thread->GetNumParams() >= 4)
    {
        GMEvent event;
        GMServer* thisObject = static_cast<GMServer*>(a_thread->GetThis()->GetUserSafe(GMServer::GetType()));

        int pluginID    = a_thread->ParamInt(0);
        int eventID     = a_thread->ParamInt(1);

        event.m_thisObject  = a_thread->Param(2);
        event.m_function    = a_thread->ParamFunction(3);

        thisObject->RegisterEventHandler(pluginID, eventID, event);
        return GM_OK;
    }
    return GM_EXCEPTION;
}


void GM_CDECL GMServer::GMOPDotGet(gmThread * a_thread, gmVariable * a_operands)
{
    GMServer* thisObject = static_cast<GMServer*>(a_operands[0].GetUserSafe(GMServer::GetType()));

    if(thisObject)
    {
        if(a_operands[1].m_value.m_ref == GMSTRING_PLAYER_COUNT->GetRef())
            a_operands[0].SetInt(thisObject->m_players.PlayerCount());

        else if(a_operands[1].m_value.m_ref == GMSTRING_PLAYERS->GetRef())
            a_operands[0].SetUser(thisObject->m_players.GetUserObject());

    }
}


void GMServer::Register(gmMachine* machine)
{
    GMSTRING_PLAYER_COUNT   = machine->AllocPermanantStringObject("playercount");
    GMSTRING_PLAYERS        = machine->AllocPermanantStringObject("players");

    m_userType = machine->CreateUserType("Server");
    machine->RegisterTypeLibraryFunction(m_userType, "RegisterEvent", GMServer::GMRegisterEvent, NULL);
    machine->RegisterUserCallbacks(m_userType, GMTrace, NULL, NULL);
}
